﻿#include "bhxjglttf.h"

BHXJGLTTFMesh::BHXJGLTTFMesh():BHXJGLMesh()
{

}
BHXJGLTTFMesh::~BHXJGLTTFMesh()
{

}

BHXJGLTTF::BHXJGLTTF()
{
    this->ttfmeshes=new std::map<unsigned short,BHXJGLTTFMesh*>();

    this->fontinfo=new stbtt_fontinfo();
    this->bytes=new QByteArray();
}
BHXJGLTTF::~BHXJGLTTF()
{
    std::map<unsigned short,BHXJGLTTFMesh*>::iterator iter=ttfmeshes->begin();
    for(;iter!=ttfmeshes->end();++iter)
    {
        delete iter->second;
    }
    std::map<unsigned short,BHXJGLTTFMesh*>().swap(*ttfmeshes);

    delete bytes;
    delete fontinfo;
}

void BHXJGLTTF::Init(BHXJGLFUN *g,const QString& path)
{
    //初始化
    this->gl=g;
    //this->shader=shader;
    QFile mFile(path);
    mFile.open(QIODevice::ReadOnly);
    //QByteArray bytes=mFile.readAll();

    bytes->clear();
    bytes->append(mFile.readAll());
    mFile.close();
    char * c=bytes->data();

    int ascent = 0;
    int lineGap = 0;
    stbtt_InitFont(fontinfo, reinterpret_cast<unsigned char*>(c), 0);
    stbtt_GetFontVMetrics(fontinfo, &ascent, &descent, &lineGap);
    fontheight = ascent - descent;
}

void BHXJGLTTF::PaintString(const QString &str,const glm::mat4 &projection,const glm::mat4 &view,const glm::mat4 &model,BHXJGLShader *shader)
{
    this->PaintMesh(str,projection,view,model,shader);
}

void BHXJGLTTF::PaintString(const QString &str,const BHXJGLPaintParam & p,BHXJGLShader *shader)
{
    this->PaintMesh(str,p.Projection(),p.View(),p.Model(),shader);
}

void BHXJGLTTF::PaintMesh(const QString &str,const glm::mat4 &projection,const glm::mat4 &view,const glm::mat4 &model,BHXJGLShader *shader)
{
    if(this->gl==nullptr||shader==nullptr)
        return;
    QString::const_iterator iter=str.begin();
    std::list<helpdatam> datalist;
    float strwidth=0;
    for(;iter!=str.end();++iter)
    {
        BHXJGLTTFMesh* m=this->getMesh(iter->unicode());
        if(m!=nullptr)
        {
            helpdatam hd;
            std::get<0>(hd)=m;
            std::get<1>(hd)=strwidth;
            datalist.push_back(hd);
            strwidth+=m->Advance;
        }
    }

    for(const helpdatam &d:datalist)
    {
        BHXJGLTTFMesh *m=std::get<0>(d);
        glm::mat4 mat4=glm::scale(model,glm::vec3(1)/fontheight);
        mat4=glm::translate(mat4,glm::vec3(-strwidth/2+std::get<1>(d),-(fontheight/2+descent),0));
        shader->use();
        shader->setMat4("model",mat4);
        shader->setMat4("view",view);
        shader->setMat4("projection",projection);
        m->Paint(shader);
    }
    this->gl->glBindVertexArray(0);
}

BHXJGLTTFMesh* BHXJGLTTF::getMesh(const unsigned short &unicode)
{
    BHXJGLTTFMesh *m=nullptr;
    auto iter = ttfmeshes->find(unicode);
    //字形已经加载
    if(iter != ttfmeshes->end())
    {
        m=iter->second;
    }
    //字形尚未加载
    else
    {
        stbtt_vertex* stbVertex = nullptr;
        TESStesselator *tess=tessNewTess(nullptr);
        m=new BHXJGLTTFMesh();
        std::unique_ptr<std::vector<glm::vec2>> cache(new std::vector<glm::vec2>());

        int verCount = stbtt_GetCodepointShape(fontinfo, unicode, &stbVertex);
        stbtt_GetCodepointHMetrics(fontinfo,unicode, &m->Advance, &m->Lsb);

        //分割多边形为三角形
        {
            glm::vec2 previous;
            glm::vec2 current;
            glm::vec2 control;

            for (int i = 0; i<verCount; i++)
            {
                stbtt_vertex* stbVer = (stbVertex+i);
                switch (stbVer->type)
                {
                case STBTT_vmove:
                    if(cache->size()>2)
                    {
                        this->addTessPolygon(tess,*cache);
                    }
                    cache->clear();
                    current.x=stbVer->x;
                    current.y=stbVer->y;
                    cache->push_back(current);
                    break;

                case STBTT_vline:
                    current.x=stbVer->x;
                    current.y=stbVer->y;
                    cache->push_back(current);
                    break;

                case STBTT_vcurve:
                {
                    previous=current;
                    current.x=stbVer->x;
                    current.y=stbVer->y;
                    control.x=stbVer->cx;
                    control.y=stbVer->cy;
                    //                    glm::vec2 t;
                    //                    int n=10;
                    //                    for(int i=0;i<n-1;++i)
                    //                    {
                    //                        getBezierPoint(t, previous,control,current,(i+1)*(1.0f/n));
                    //                        cache.push_back(t);
                    //                    }
                    //                    cache.push_back(current);
                    getBezierPoint_stbttf(*cache,previous.x,previous.y,control.x,control.y,current.x,current.y,0.35f*0.35f,0);
                }
                break;

                default:break;
                }
            }
            if(cache->size()>2)
            {
                this->addTessPolygon(tess,*cache);
                cache->clear();
            }
            float normal[3];
            normal[0]=0;
            normal[1]=0;
            normal[2]=1;
            tessTesselate(tess,TessWindingRule::TESS_WINDING_ODD,TessElementType::TESS_POLYGONS,3,2,normal);
        }

        //生成ttfmesh
        {
            const float* verticestess=tessGetVertices(tess);
            int verticestessnum=tessGetVertexCount(tess);
            const int * elements=tessGetElements(tess);
            int elementnum=tessGetElementCount(tess);
            m->trianglescount=static_cast<unsigned int>(elementnum);

            BHXJGLVertex v;
            for(int i=0;i<verticestessnum;++i)
            {
                v.position=glm::vec3(verticestess[i*2],verticestess[i*2+1],0);
                v.normal=glm::vec3(0,0,1);
                //v.texcoodr
                m->vertices.push_back(v);
            }
            for(int i=0;i<elementnum*3;++i)
            {
                m->indices.push_back(static_cast<unsigned int>(elements[i]));
            }

            m->Init(gl);

        }
        stbtt_FreeShape(fontinfo,stbVertex);
        tessDeleteTess(tess);

        ttfmeshes->emplace(unicode,m);
    }

    return m;
}

void BHXJGLTTF::addTextToMesh(BHXJGLTTFMesh *const m,const unsigned short &unicode,const glm::mat4 &model)
{
    stbtt_vertex* stbVertex = nullptr;
    TESStesselator *tess=tessNewTess(nullptr);
    std::unique_ptr<std::vector<glm::vec2>> cache(new std::vector<glm::vec2>());

    int verCount = stbtt_GetCodepointShape(fontinfo, unicode, &stbVertex);
    stbtt_GetCodepointHMetrics(fontinfo,unicode, &m->Advance, &m->Lsb);


    glm::mat4 mat4=glm::scale(model,glm::vec3(1)/fontheight);
    mat4=glm::translate(mat4,glm::vec3(-m->Advance/2,-(fontheight/2+descent),0));

    //点集和侧边
    {
        glm::vec2 previous;
        glm::vec2 current;
        glm::vec2 control;

        for (int i = 0; i<verCount; i++)
        {
            stbtt_vertex* stbVer = (stbVertex+i);
            switch (stbVer->type)
            {
            case STBTT_vmove:
                if(cache->size()>2)
                {
                    this->addTessPolygon(tess,*cache);
                    this->addTextStripe(m,*cache,mat4);

                    //i=verCount;
                }
                cache->clear();
                current.x=stbVer->x;
                current.y=stbVer->y;
                cache->push_back(current);
                break;

            case STBTT_vline:
                current.x=stbVer->x;
                current.y=stbVer->y;
                cache->push_back(current);
                break;

            case STBTT_vcurve:
            {
                previous=current;
                current.x=stbVer->x;
                current.y=stbVer->y;
                control.x=stbVer->cx;
                control.y=stbVer->cy;

                getBezierPoint_stbttf(*cache,previous.x,previous.y,control.x,control.y,current.x,current.y,0.35f*0.35f,0);
            }
            break;

            default:break;
            }
        }
        if(cache->size()>2)
        {
            this->addTessPolygon(tess,*cache);  
            this->addTextStripe(m,*cache,mat4);
            cache->clear();
        }
    }

    //生成字形顶点和索引
    {
        float normal[3]={0,0,1};
        tessTesselate(tess,TessWindingRule::TESS_WINDING_ODD,TessElementType::TESS_POLYGONS,3,2,normal);

        const float* verticestess=tessGetVertices(tess);
        int verticestessnum=tessGetVertexCount(tess);
        const int * elements=tessGetElements(tess);
        int elementnum=tessGetElementCount(tess);

        //正面
        {
            unsigned int startid=m->vertices.size();
            m->trianglescount+=static_cast<unsigned int>(elementnum);
            BHXJGLVertex v;
            for(int i=0;i<verticestessnum;++i)
            {
                v.position=glm::vec3(mat4*glm::vec4(verticestess[i*2],verticestess[i*2+1],0,1));
                v.normal=  glm::normalize(glm::vec3(mat4*glm::vec4(0,0,1,0)));
                //v.texcoodr
                m->vertices.push_back(v);
            }
            for(int i=0;i<elementnum*3;++i)
            {
                m->indices.push_back(static_cast<unsigned int>(elements[i])+startid);
            }
        }
        //背面
        {
            unsigned int startid=m->vertices.size();
            m->trianglescount+=static_cast<unsigned int>(elementnum);
            BHXJGLVertex v;
            for(int i=0;i<verticestessnum;++i)
            {
                v.position=glm::vec3(mat4*glm::vec4(verticestess[i*2],verticestess[i*2+1],-fontheight,1));
                v.normal=  glm::normalize(glm::vec3(mat4*glm::vec4(0,0,-1,0)));
                //v.texcoodr
                m->vertices.push_back(v);
            }
            for(int i=elementnum*3-1;i>-1;--i)
            {
                m->indices.push_back(static_cast<unsigned int>(elements[i])+startid);
            }
        }
    }

    stbtt_FreeShape(fontinfo,stbVertex);
    tessDeleteTess(tess);
}


void BHXJGLTTF::addTextStripe(BHXJGLMesh *m,std::vector<glm::vec2> &cache,const glm::mat4 &mat4)
{
    unsigned int startid=m->vertices.size();
    unsigned int num=cache.size()-1;
    bool clockwise= BHXJGLModelHelper::testClockWise(cache);
    //cache.push_back(cache[0]);
    BHXJGLVertex v;
    for (unsigned int i=0;i<cache.size();++i)
    {
        v.position=glm::vec3(mat4*glm::vec4(cache[i].x,cache[i].y,0,1));
        m->vertices.push_back(v);
    }
    for (unsigned int i=0;i<cache.size();++i)
    {
        v.position=glm::vec3(mat4*glm::vec4(cache[i].x,cache[i].y,-fontheight,1));
        m->vertices.push_back(v);
    }

    BHXJGLModelHelper::addStripeToMesh(m,startid,startid+num+1,num,clockwise);

    for (unsigned int i=0;i<num+1;++i)
    {
        unsigned int idnext=i==num?1:i+1;
        unsigned int idprevious=i==0?num-1:i-1;
        unsigned int id=startid+i;
        idnext+=startid;
        idprevious+=startid;

        glm::vec3 v=m->vertices[id+num+1].position-m->vertices[id].position;
        glm::vec3 vn=m->vertices[idnext].position-m->vertices[id].position;
        glm::vec3 vp=m->vertices[idprevious].position-m->vertices[id].position;

        glm::vec3 np=glm::normalize(glm::cross(vp,v));
        glm::vec3 nn=glm::normalize(glm::cross(v,vn));

        glm::vec3 normal=glm::normalize(np+nn);
        if(BHXJCW==clockwise)
        {
            normal=-normal;
        }

        m->vertices[id].normal=normal;
        m->vertices[id+num+1].normal=normal;
    }


}

void BHXJGLTTF::addTessPolygon(TESStesselator *tess,const std::vector<glm::vec2> &cache)
{
    if(cache.size()>2)
    {
        void * p=malloc(cache.size()*2*sizeof(float));
        float *pf=reinterpret_cast<float*>(p);
        for(const glm::vec2 &v:cache)
        {
            *pf=v.x;++pf;
            *pf=v.y;++pf;
        }
        tessAddContour(tess,2,p,2*sizeof(float),static_cast<int>(cache.size()));
        free(p);
    }
}

void BHXJGLTTF::getBezierPoint(glm::vec2 &ret,glm::vec2 &previous,glm::vec2 &contrl,glm::vec2 &current,float  scale)
{
    ret.x = (1-scale)*(1-scale)*previous.x + 2*scale*(1-scale)*contrl.x + scale*scale*current.x;
    ret.y = (1-scale)*(1-scale)*previous.y + 2*scale*(1-scale)*contrl.y + scale*scale*current.y;
}

///这个函数是从stb库中搬来的感觉比上边那个好就用这个了
int BHXJGLTTF::getBezierPoint_stbttf(std::vector<glm::vec2> &cache, float x0, float y0, float x1, float y1, float x2, float y2, float objspace_flatness_squared, int n)
{
    // midpoint
    float mx = (x0 + 2*x1 + x2)/4;
    float my = (y0 + 2*y1 + y2)/4;
    // versus directly drawn line
    float dx = (x0+x2)/2 - mx;
    float dy = (y0+y2)/2 - my;
    if (n > 16) // 65536 segments on one curve better be enough!
        return 1;
    if (dx*dx+dy*dy > objspace_flatness_squared)
    { // half-pixel error allowed... need to be smaller if AA
        getBezierPoint_stbttf(cache, x0,y0, (x0+x1)/2.0f,(y0+y1)/2.0f, mx,my, objspace_flatness_squared,n+1);
        getBezierPoint_stbttf(cache, mx,my, (x1+x2)/2.0f,(y1+y2)/2.0f, x2,y2, objspace_flatness_squared,n+1);
    }
    else
    {
        glm::vec2 v;
        v.x=x2;
        v.y=y2;
        cache.push_back(v);
    }
    return 1;
}
